package cast

import (
	"fmt"
	"strings"

	"github.com/huandu/go-clone"

	"gitee.com/u-language/u-language/ucom/ast"
	"gitee.com/u-language/u-language/ucom/astdata"
	"gitee.com/u-language/u-language/ucom/data"
	"gitee.com/u-language/u-language/ucom/enum"
	"gitee.com/u-language/u-language/ucom/internal/errutil"
	"gitee.com/u-language/u-language/ucom/internal/utils"
)

// generateInAutoFreeFuncDecl 生成在自动释放块内被调用的函数声明
func generateInAutoFreeFuncDecl(symbol ast.SymbolInfo, buf *strings.Builder) {
	var info *ast.FuncInfo
	switch n := symbol.Info.(type) {
	case *ast.FuncInfo:
		info = n
	case *ast.MethodNode:
		info = n.FuncInfo
	default:
		panic(fmt.Errorf("未知的类型：%+v", symbol))
	}
	if len(info.RetValue) != 0 {
		buf.WriteString(typeToC(info.RetValue[0].Type.Typ()))
	} else {
		buf.WriteString("void")
	}
	buf.WriteString(" ")
	buf.WriteString(info.Name)
	buf.WriteString(enum.AutoFreeInFunc)
	buf.WriteString("(mempool__Mempool* memPooL")
	if len(info.Parame) != 0 {
		buf.WriteString(",")
	}
	for i := 0; i < len(info.Parame); i++ { //遍历每一个参数，生成参数列表
		buf.WriteString(typeToC(info.Parame[i].Type.Typ()))
		buf.WriteString(" ")
		buf.WriteString(info.Parame[i].Name)
		if i+1 < len(info.Parame) {
			buf.WriteString(", ")
		}
	}
	buf.WriteString(");\n")
}

// generateInAutoFreeFunc 生成在自动释放块内被调用的函数
func generateInAutoFreeFunc(InAutoFreeFuncCallName *data.RemoveDupStrck[ast.FuncNameNode], findAstTree func(string) *ast.Tree, findUtoc func(string) *UtoC, sbt *ast.Sbt, inAutoFreeFuncInfo *[]ast.SymbolInfo, PackageName string) []ast.Node {
	var unode []ast.Node
	var cnode []ast.Node
	ret := make([]ast.Node, 0)
	*inAutoFreeFuncInfo = grow(*inAutoFreeFuncInfo, InAutoFreeFuncCallName.Len())
	InAutoFreeFuncCallName.Range(func(s ast.FuncNameNode) {
		var info ast.SymbolInfo
		if o, ok := s.(*ast.Object); ok {
			info = sbt.Have(o.Name)
		} else {
			o := s.(*ast.Objects)
			if o.IsImportSymbol {
				info = sbt.Have(utils.GeneratePackageSymbol(o.Slice[0].Name, o.Slice[1].Name))
			} else {
				info = sbt.HaveMethod(o.T, o.Slice[len(o.Slice)-1].Name)
			}
		}
		*inAutoFreeFuncInfo = append(*inAutoFreeFuncInfo, info)
		finfo, ismethod, typ := ret_info_to_autofree(info)
		astTree := findAstTree(finfo.FileName)
		unode = astTree.Nodes
		Tree := findUtoc(finfo.FileName)
		cnode = Tree.Nodes
		name, left, right := ret_node_to_autofree(unode, finfo)
		f := generateAutoFreeFuncNode(name, typ, ismethod)
		f.Parame, f.RetValue = finfo.Parame, finfo.RetValue
		f.Parame = append([]astdata.Parame{astdata.NewNameAndType("memPooL", mempoolType)}, f.Parame...)
		grow(ret, right-left+1)
		ret = append(ret, f)
		auto_inAutoFree_call_slice(left, right, cnode, &ret, InAutoFreeFuncCallName)
	})
	return ret
}

var mempoolType = ast.NewObject(ast.TypeObj, "mempool__Mempool*")

var parame1 = ast.NewObject(ast.SymbolObj, "memPooL")

func auto_inAutoFree_call_slice(left, right int, cnode []ast.Node, ret *[]ast.Node, InAutoFreeFuncCallName *data.RemoveDupStrck[ast.FuncNameNode]) {
	for i := left; i <= right; i++ {
		*ret = append(*ret, auto_inAutoFree_call(cnode[i], parame1, InAutoFreeFuncCallName))
	}
}

func auto_inAutoFree_call(n ast.Node, parame1 ast.Expr, InAutoFreeFuncCallName *data.RemoveDupStrck[ast.FuncNameNode]) ast.Node {
	if n == nil {
		return n
	}
	switch tmp1 := n.(type) {
	case *ast.VarNode:
		tmp := *tmp1
		tmp.Value = auto_inAutoFree_Expr(tmp.Value, parame1, InAutoFreeFuncCallName)
		return &tmp
	case *ast.ASSIGNNode:
		tmp := *tmp1
		tmp.Dest = auto_inAutoFree_Expr(tmp.Dest, parame1, InAutoFreeFuncCallName)
		tmp.Src = auto_inAutoFree_Expr(tmp.Src, parame1, InAutoFreeFuncCallName)
		return &tmp
	case *ast.OpExpr:
		tmp := *tmp1
		tmp.Src1 = auto_inAutoFree_Expr(tmp.Src1, parame1, InAutoFreeFuncCallName)
		tmp.Src2 = auto_inAutoFree_Expr(tmp.Src2, parame1, InAutoFreeFuncCallName)
		return &tmp
	case *ast.IfNode:
		tmp := *tmp1
		tmp.BoolExpr = auto_inAutoFree_Expr(tmp.BoolExpr, parame1, InAutoFreeFuncCallName)
		return &tmp
	case *ast.ElseNode:
		tmp := *tmp1
		tmp.BoolExpr = auto_inAutoFree_Expr(tmp.BoolExpr, parame1, InAutoFreeFuncCallName)
		return &tmp
	case *ast.ForNode:
		tmp := *tmp1
		tmp.InitStmt = auto_inAutoFree_call(tmp.InitStmt, parame1, InAutoFreeFuncCallName)
		tmp.BoolExpr = auto_inAutoFree_Expr(tmp.BoolExpr, parame1, InAutoFreeFuncCallName)
		tmp.EndStmt = auto_inAutoFree_call(tmp.EndStmt, parame1, InAutoFreeFuncCallName)
		return &tmp
	case *ast.ReturnNode:
		tmp := *tmp1
		tmp.RetValue = auto_inAutoFree_Expr(tmp.RetValue, parame1, InAutoFreeFuncCallName)
		return &tmp
	case *ast.ConstNode:
		tmp := *tmp1
		tmp.Value = auto_inAutoFree_Expr(tmp.Value, parame1, InAutoFreeFuncCallName)
		return &tmp
	case *ast.SelfOpStmt:
		tmp := *tmp1
		tmp.Dest = auto_inAutoFree_Expr(tmp.Dest, parame1, InAutoFreeFuncCallName)
		return &tmp
	case *ast.SwitchNode:
		tmp := *tmp1
		tmp.Expr = auto_inAutoFree_Expr(tmp.Expr, parame1, InAutoFreeFuncCallName)
		return &tmp
	case *ast.CaseNode:
		tmp := *tmp1
		tmp.Expr = auto_inAutoFree_Expr(tmp.Expr, parame1, InAutoFreeFuncCallName)
		return &tmp
	case *includeNode, ast.RbraceNode, *ast.GotoStmt, *ast.LabelNode, ast.BreakStmt, ast.ContinueStmt, *ast.StructDecl, *ast.DefaultNode, *ast.EnumDecl: //这些无需处理,因为里面不会有函数调用
	}
	return n
}

var mempoolObjNew = ast.NewObject(ast.SymbolObj, enum.MemPoolNew)

func auto_inAutoFree_Expr(n ast.Expr, parame1 ast.Expr, InAutoFreeFuncCallName *data.RemoveDupStrck[ast.FuncNameNode]) ast.Expr {
	if n == nil {
		return n
	}
	switch n := n.(type) {
	case *ast.Object, *ast.Objects: //这些无需处理,因为里面不会有函数调用
	case *ast.CallNode:
		var funcname string
		if o, ok := n.FuncName.(*ast.Object); ok {
			funcname = o.Name
		}
		if funcname != enum.Malloc && funcname != enum.MallocSize { //如果不是调用malloc或mallocSize
			if _, ok := ast.Builtin_func_info[funcname]; !ok {
				InAutoFreeFuncCallName.Add(n.FuncName)
			}
			return n
		}
		n = clone.Clone(n).(*ast.CallNode)
		if funcname == enum.Malloc {
			switch o := n.Parame[0].(type) {
			case *ast.Object:
				o.Kind = ast.TypeObj
			case *ast.Objects:
				o.Slice[0].Kind = ast.TypeObj
			default:
				panic("未知的类型")
			}
		}
		n.CCallFuncName, n.FuncName = enum.MemPoolNew, mempoolObjNew
		n.Parame = append([]ast.Expr{parame1}, n.Parame...)
		return n
	case *ast.IndexExpr:
		tmp := *n
		tmp.X = auto_inAutoFree_Expr(tmp.X, parame1, InAutoFreeFuncCallName)
		tmp.Index = auto_inAutoFree_Expr(tmp.Index, parame1, InAutoFreeFuncCallName)
		return &tmp
	case *ast.Dereference:
		tmp := *n
		tmp.Value = auto_inAutoFree_Expr(tmp.Value, parame1, InAutoFreeFuncCallName)
		return &tmp
	default:
		panic(errutil.NewErr2(errutil.CompileErr, fmt.Sprintf("未知的节点：%+v", n)))
	}
	return n
}

// 复制自go1.21标准库
// TODO:当最低需要go1.21时，改为标准库实现
func grow[S ~[]E, E any](s S, n int) S {
	if n < 0 {
		panic("cannot be negative")
	}
	if n -= cap(s) - len(s); n > 0 {
		s = append(s[:cap(s)], make([]E, n)...)[:len(s)]
	}
	return s
}

// ret_info_to_autofree 返回信息中自动释放块需要的
func ret_info_to_autofree(info ast.SymbolInfo) (*ast.FuncInfo, bool, string) {
	switch i := info.Info.(type) {
	case *ast.FuncInfo:
		return i, false, ""
	case *ast.MethodNode:
		return i.FuncInfo, true, i.Typ
	default:
		panic(fmt.Errorf("未知的类型：%+v", info))
	}
}

// ret_node_to_autofree 返回节点中中自动释放块需要的
func ret_node_to_autofree(unode []ast.Node, info *ast.FuncInfo) (name string, left int, right int) {
	switch n := unode[info.LineNum-1].(type) {
	case *ast.FuncNode:
		name, left, right = n.Name, n.Left, n.Right
	case *ast.MethodNode:
		name, left, right = n.FuncInfo.Name, n.Left, n.Right
	default:
		panic(fmt.Errorf("未知的类型：%+v", n))
	}
	return
}

// generateAutoFreeFuncNode 生成在自动释放块内的函数节点
func generateAutoFreeFuncNode(name string, typ string, ismethod bool) (f *ast.FuncNode) {
	f = &ast.FuncNode{}
	f.FuncInfo = &ast.FuncInfo{}
	var buf strings.Builder
	if ismethod {
		buf.WriteString(enum.Method)
		buf.WriteString("_")
		buf.WriteString(typ)
		buf.WriteString("_")
	}
	buf.WriteString(name)
	buf.WriteString(enum.AutoFreeInFunc)
	f.Name = buf.String()
	return
}
